package com.cch.cooperation.biz.common.lock;

import com.cch.cooperation.biz.common.util.ExpressUtil;
import com.cch.cooperation.common.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.*;


/**
 * 锁注解拦截器
 *
 * @author cch
 **/
@Order(100)
@Aspect
@Slf4j
@Component
public class LockerAspect {

    @Resource
    private RedissonClient redissonClient;

    @Around("@annotation(com.cch.cooperation.biz.common.lock.Locker)")
    public Object aroundLocker(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取注解信息
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = joinPoint.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        Locker locker = method.getAnnotation(Locker.class);
        // 将参数转为 context
        EvaluationContext context = new StandardEvaluationContext();
        String[] parameterNames = methodSignature.getParameterNames();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], joinPoint.getArgs()[i]);
        }
        // 根据 key 按顺序创建锁
        List<RLock> locks = new ArrayList<>();
        Set<String> keys = new LinkedHashSet<>();
        keys.add(locker.key());
        keys.addAll(Arrays.asList(locker.keys()));
        for (String key : keys) {
            key = ExpressUtil.format(key);
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(key);
            key = expression.getValue(context, String.class);
            locks.add(redissonClient.getLock(key));
        }
        // 加锁
        try {
            for (RLock lock : locks) {
                if (!lock.tryLock(locker.waitTimeout(), locker.timeout(), locker.timeUnit())) {
                    throw new BizException("当前系统正忙，请稍后再试");
                }
            }
        } catch (InterruptedException e) {
            log.error("锁等待被中断", e);
            throw new BizException("当前系统正忙，请稍后再试");
        }
        try {
            // 执行回调函数
            return joinPoint.proceed(joinPoint.getArgs());
        } finally {
            // 解锁 反转集合，解锁要后向前
            Collections.reverse(locks);
            Runnable unlockCall = () -> {
                for (RLock lock : locks) {
                    if (!lock.isHeldByCurrentThread()) {
                        log.warn("[锁超时警告]lock key:{},cb:{}的锁方法执行时间超过锁超时，请调整锁超时时间！！", lock.getName(), method.getName());
                    } else {
                        lock.unlockAsync();
                    }
                }
            };
            invokeUnlock(unlockCall);
        }
    }

    private void invokeUnlock(Runnable runnable) {
        if (!TransactionSynchronizationManager.isActualTransactionActive()) {
            runnable.run();
        } else {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    runnable.run();
                }
            });
        }
    }
}
